package globulin;

import jgame.*;
import jgame.platform.*;
import java.awt.event.KeyEvent.*;
import globulin.soundFXProccessing.*;

/** This class is the main game engine.
 * @author Sean Pavlichek @ Jonathan Horvath
 */
public class Globulin extends StdGame {
    public static final int PLAYER_COLID = 1;
    public static final int PLAYER_LASER_COLID = 2;
    public static final int ENEMY_COLID = 3;
    public static final int ENEMY_MISSILE_COLID = 4;
    public static final int POWERUP_COLID = 5;

    /**
     * The amount of points needed to earn a new life
     */
    protected final int NEW_LIFE_SCORE = 2500;
    /**
     * The amount of points needed to spawn a new power up
     */
    protected final int SPAWN_POWERUP_POINTS = 500;
    /**
     * The number of levels until boss appears
     */
    protected final int BOSS_LEVEL_COUNT = 3;

    // Other necessary variables
    private static int WindowSize_x = 640;
    private static int WindowSize_y = 640;
    private int numLivesGained = 0;
    public boolean bossAlive = false;
    public boolean bossKilled = false;
    Player player1Ship;
    MusicPlayer jukebox = new MusicPlayer(this);
    WorkQueue soundFxThreadPool = new WorkQueue(4);
    int PowerUpCount = 1;

    /**
     * Main entry point into program
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // Window size can be determined by arguments
        if (args.length == 2) {
            new Globulin(parseSizeArgs(args, 0));
        } else {
            new Globulin(new JGPoint(WindowSize_x, WindowSize_y));
        }
    }

    /**
     * Basic constructor that uses a default size.
     */
    public Globulin() {
        initEngineApplet();
    }

    /**
     * Basic constructor that takes in the size of the playing field.
     * @param size The window size.
     */
    public Globulin(JGPoint size) {
        initEngine(size.x, size.y);
    }

    /**
     * Initializes the canvas of the game.
     */
    public void initCanvas() {
        setCanvasSettings((WindowSize_x / 32), (WindowSize_y / 32), 32, 32, null, null, null);
    }

    /**
     * Gets the level that the player is on.
     * @return The current level.
     */
    public int getLevel() {
        return level;
    }

    /**
     * Initializes the game.
     */
    public void initGame() {
        // Set the framerate of the game
        setFrameRate(60, 2);

        // Set the media definition file
        defineMedia("media/media.tbl");

        // load the background image
        setBackGroundImageForLevel();

        // Enlarge the font sized used on the screen
        status_font = new JGFont("Courier",0,24);

        // Load the saved high scores
        setHighscores(10, new Highscore(0, "Player1"), 15);

        key_fire = ' ';
        key_continuegame = 'C';

        startgame_ingame = true;
        jukebox.selectRandomSong();
    }

    /**
     * Generates the player's character.
     */
    @Override
    public void initNewLife() {
        player1Ship = new Player(this, pfWidth() / 2, pfHeight() - 100, 2);
    }

    /**
     * Method for starting the game over.
     */
    @Override
    public void startGameOver() {
        removeObjects(null, 0);

        PowerUpCount = 1;

        // load the background image
        setBackGroundImageForLevel();
    }

    /**
     * Executes every frame
     */
    public void doFrameInGame() {
        // Moves the objects
        moveObjects();

        // Checks for collision between the player and enemies
        checkCollision(ENEMY_COLID, PLAYER_COLID); // enemies hit player
        // Checks for collision between the player and enemies' missiles
        checkCollision(ENEMY_MISSILE_COLID, PLAYER_COLID); // enemies hit player
        // Checks for collision between the enemies and the player's shots
        checkCollision(PLAYER_LASER_COLID, ENEMY_COLID); // bullets hit enemies

        // Check to see if the player life is lost
        if (!player1Ship.isAlive()) {
            lifeLost();
        }

        // Spawns enemies at certain intervals
        if (checkTime(0, 1600, (32 - (level * 2)))) {
            if ((random(0, 20, 1) % 2) == 1) {
                spawnFormation(random(3, 5, 1));
            } else {
                new Enemy(this, random(0,1,1));
            }
        }

        // Spawns powerups if there are none
        if (score > PowerUpCount * SPAWN_POWERUP_POINTS) {
            PowerUpCount++;
            new PowerUp(random(0, pfWidth(), 1));
        }

        // Checks to see if the level is over
        if (gametime >= 1600 && countObjects("enemy", 0) == 0) {
            // If there was a boss and it is still alive
            if ((level + 1) % BOSS_LEVEL_COUNT == 0 && !bossAlive && !bossKilled) {
                bossAlive = true;
                new Boss(this);
            }

            // If there was a boss and it was killed
            if ((level + 1) % BOSS_LEVEL_COUNT == 0 && bossKilled) {
                bossKilled = false;
                levelDone();
                // If there was no boss
            } else if ((level + 1) % BOSS_LEVEL_COUNT != 0) {
                levelDone();
            }
        }
    }

    /**
     * Increments the level of the game.
     */
    @Override
    public void incrementLevel() {

        // Adds 50 to the player's score for completing the previous level
        score += 50;

        // Goes to the next level
        level++;

        // Increments the stage counter
        stage++;

        // load the background image
        setBackGroundImageForLevel();
    }

    /**
     * Check to see if the player deserves a new life based on score.
     */
    public void checkScoreForNewLife() {
        if ((score - (numLivesGained * NEW_LIFE_SCORE)) > NEW_LIFE_SCORE) {
            numLivesGained += 1;
            lives++;
        }
    }

    /**
     * Spawn a new formation of enemies
     * @param formation Identifies the formation
     */
    protected void spawnFormation(int formation) {
        (new Enemy(this, null, 1, formation, random(0,1,1))).addFollower();
    }

    /**
     * Set the background for the game
     */
    protected void setBackGroundImageForLevel() {

        // The background changes after a boss has been destroyed
        switch (level / BOSS_LEVEL_COUNT) {
            case 1:
                defineImage("bgimage", "-", 0, "media/background/brain_waves.gif", "-");
                break;
            default:
                defineImage("bgimage", "-", 0, "media/background/blood_plasma.jpg", "-");
                
        }

        setBGImage("bgimage");
    }
}
